#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Hash Storage GUI界面"""

import sys
import os
from pathlib import Path
from typing import Dict, List, Optional
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget,
    QTreeWidget, QTreeWidgetItem, QPushButton, QLabel, QLineEdit,
    QMessageBox, QFileDialog, QMenu, QAction, QHeaderView,
    QProgressBar, QStatusBar, QSplitter, QDialog, QFormLayout, QDialogButtonBox, QTextEdit
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QIcon, QFont, QTextOption

from hash_storage import HashStorage


class UploadWorker(QThread):
    """Upload worker thread"""
    progress = pyqtSignal(str)  # Progress information
    progress_value = pyqtSignal(int)  # Progress percentage (0-100)
    finished = pyqtSignal(bool, str)  # Completion signal (success, message)
    
    def __init__(self, storage: HashStorage, file_path: str, custom_name: str = None):
        super().__init__()
        self.storage = storage
        self.file_path = file_path
        self.custom_name = custom_name
        self.total_size = 0
        self.uploaded_size = 0
    
    def run(self):
        try:
            from pathlib import Path
            path = Path(self.file_path)
            
            # Calculate total size for progress tracking
            self.total_size = self._calculate_total_size(path)
            self.uploaded_size = 0
            
            self.progress.emit(f"Starting upload: {self.file_path}")
            self.progress_value.emit(0)
            
            # Upload with progress tracking
            if path.is_file():
                success = self._upload_file_with_progress(path)
            elif path.is_dir():
                success = self._upload_directory_with_progress(path)
            else:
                success = False
                
            if success:
                self.progress_value.emit(100)
                self.finished.emit(True, f"Upload completed: {self.file_path}")
            else:
                self.finished.emit(False, f"Upload failed: {self.file_path}")
        except Exception as e:
            self.finished.emit(False, f"Upload error: {str(e)}")
    
    def _calculate_total_size(self, path):
        """Calculate total size for upload"""
        try:
            if path.is_file():
                return path.stat().st_size
            elif path.is_dir():
                total_size = 0
                for file_path in path.rglob('*'):
                    if file_path.is_file():
                        total_size += file_path.stat().st_size
                return total_size
            return 0
        except Exception:
            return 0
    
    def _upload_file_with_progress(self, file_path):
        """Upload single file with progress"""
        try:
            success = self.storage.upload(str(file_path), self.custom_name)
            if success:
                self.uploaded_size = self.total_size
                self._update_progress()
            return success
        except Exception:
            return False
    
    def _upload_directory_with_progress(self, dir_path):
        """Upload directory with progress tracking"""
        try:
            meta_data = self.storage._get_meta_data()
            custom_dirname = self.custom_name or dir_path.name
            
            file_count = 0
            total_files = sum(1 for f in dir_path.rglob('*') if f.is_file())
            
            for file_path in dir_path.rglob('*'):
                if file_path.is_file():
                    try:
                        # Calculate relative path as filename
                        relative_path = file_path.relative_to(dir_path)
                        filename = custom_dirname + "/" + str(relative_path).replace('\\', '/')
                        
                        # Upload single file
                        self.storage._upload_single_file(file_path, filename, meta_data)
                        
                        # Update progress
                        file_size = file_path.stat().st_size
                        self.uploaded_size += file_size
                        self._update_progress()
                        
                        file_count += 1
                        self.progress.emit(f"Uploaded: {filename} ({file_count}/{total_files})")
                        
                    except Exception as e:
                        self.progress.emit(f"Error uploading {file_path.name}: {str(e)}")
            
            # Upload metadata
            self.storage._upload_meta_data(meta_data)
            return True
        except Exception:
            return False
    
    def _update_progress(self):
        """Update progress percentage"""
        if self.total_size > 0:
            percentage = int((self.uploaded_size / self.total_size) * 100)
            self.progress_value.emit(min(percentage, 100))


class DownloadWorker(QThread):
    """Download worker thread"""
    progress = pyqtSignal(str)  # Progress information
    progress_value = pyqtSignal(int)  # Progress percentage (0-100)
    finished = pyqtSignal(bool, str)  # Completion signal (success, message)
    
    def __init__(self, storage: HashStorage, filename: str, output_path: str):
        super().__init__()
        self.storage = storage
        self.filename = filename
        self.output_path = output_path
        self.total_size = 0
        self.downloaded_size = 0
    
    def run(self):
        try:
            # Calculate total size for progress tracking
            self.total_size = self._calculate_total_size()
            self.downloaded_size = 0
            
            self.progress.emit(f"Starting download: {self.filename}")
            self.progress_value.emit(0)
            
            # Check if it's a directory or file
            meta_data = self.storage._get_meta_data()
            if self._is_directory(self.filename, meta_data):
                success = self._download_directory_with_progress(meta_data)
            else:
                success = self._download_file_with_progress()
                
            if success:
                self.progress_value.emit(100)
                self.finished.emit(True, f"Download completed: {self.filename}")
            else:
                self.finished.emit(False, f"Download failed: {self.filename}")
        except Exception as e:
            self.finished.emit(False, f"Download error: {str(e)}")
    
    def _calculate_total_size(self):
        """Calculate total size for download"""
        try:
            meta_data = self.storage._get_meta_data()
            total_size = 0
            
            if self._is_directory(self.filename, meta_data):
                # Calculate directory size
                dir_prefix = self.filename.rstrip('/') + '/'
                for file_path in meta_data.get('file_to_hash', {}).keys():
                    if file_path.startswith(dir_prefix):
                        file_info = self.storage.get_file_info(file_path)
                        if file_info:
                            total_size += file_info['size']
            else:
                # Single file
                file_info = self.storage.get_file_info(self.filename)
                if file_info:
                    total_size = file_info['size']
                    
            return total_size
        except Exception:
            return 0
    
    def _is_directory(self, filename, meta_data):
        """Check if filename is a directory"""
        dir_prefix = filename.rstrip('/') + '/'
        for file_path in meta_data.get('file_to_hash', {}).keys():
            if file_path.startswith(dir_prefix):
                return True
        return False
    
    def _download_file_with_progress(self):
        """Download single file with progress"""
        try:
            success = self.storage.download(self.filename, self.output_path)
            if success:
                self.downloaded_size = self.total_size
                self._update_progress()
            return success
        except Exception:
            return False
    
    def _download_directory_with_progress(self, meta_data):
        """Download directory with progress tracking"""
        try:
            from pathlib import Path
            output_path = Path(self.output_path)
            dir_prefix = self.filename.rstrip('/') + '/'
            
            # Get all files in directory
            matching_files = []
            for file_path in meta_data.get('file_to_hash', {}).keys():
                if file_path.startswith(dir_prefix):
                    matching_files.append(file_path)
            
            success_count = 0
            total_count = len(matching_files)
            
            for file_path in matching_files:
                try:
                    # Calculate relative path
                    relative_path = file_path[len(dir_prefix):]
                    target_path = output_path / relative_path
                    
                    # Download single file
                    if self.storage._download_single_file(file_path, target_path, meta_data):
                        success_count += 1
                        # Update progress based on file size
                        file_info = self.storage.get_file_info(file_path)
                        if file_info:
                            self.downloaded_size += file_info['size']
                            self._update_progress()
                        
                        self.progress.emit(f"Downloaded: {file_path} ({success_count}/{total_count})")
                    else:
                        self.progress.emit(f"Failed to download: {file_path}")
                        
                except Exception as e:
                    self.progress.emit(f"Error downloading {file_path}: {str(e)}")
            
            return success_count == total_count
        except Exception:
            return False
    
    def _update_progress(self):
        """Update progress percentage"""
        if self.total_size > 0:
            percentage = int((self.downloaded_size / self.total_size) * 100)
            self.progress_value.emit(min(percentage, 100))


class HashStorageGUI(QMainWindow):
    """Hash Storage GUI main window"""
    
    def __init__(self):
        super().__init__()
        self.storage = None
        self.meta_data = {}
        self.download_worker = None
        self.upload_worker = None
        
        self.init_ui()
        self.setup_connections()
        self.load_default_config()
        
    def get_modern_stylesheet(self):
        """Get modern stylesheet for the application"""
        return """
        /* Main Window Styling */
        QMainWindow {
            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                      stop: 0 #f8f9fa, stop: 1 #e9ecef);
            color: #212529;
            font-family: "Segoe UI", "SF Pro Display", "Helvetica Neue", Arial, sans-serif;
        }
        
        #centralWidget {
            background: transparent;
        }
        
        /* Header Section */
        #headerWidget {
            background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,
                                      stop: 0 #667eea, stop: 1 #764ba2);
            border-radius: 12px;
            padding: 20px;
            margin-bottom: 10px;
        }
        
        #headerTitle {
            color: white;
            font-size: 24px;
            font-weight: 600;
            margin: 0;
        }
        
        #headerSubtitle {
            color: rgba(255, 255, 255, 0.8);
            font-size: 14px;
            margin: 5px 0 0 0;
        }
        
        #connectionStatus {
            background: rgba(255, 255, 255, 0.2);
            border: 1px solid rgba(255, 255, 255, 0.3);
            border-radius: 20px;
            padding: 8px 16px;
            color: white;
            font-weight: 500;
        }
        
        /* Splitter */
        #mainSplitter::handle {
            background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,
                                      stop: 0 #e9ecef, stop: 0.5 #dee2e6, stop: 1 #e9ecef);
            border-radius: 4px;
            margin: 2px;
        }
        
        #mainSplitter::handle:hover {
            background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,
                                      stop: 0 #007bff, stop: 1 #0056b3);
        }
        
        /* Tree Widget */
        QTreeWidget {
            background: white;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            padding: 8px;
            font-size: 13px;
            selection-background-color: #e3f2fd;
            alternate-background-color: #f8f9fa;
        }
        
        QTreeWidget::item {
            padding: 8px 4px;
            border-bottom: 1px solid #f1f3f4;
            min-height: 24px;
        }
        
        QTreeWidget::item:hover {
            background: #f0f8ff;
            border-radius: 4px;
        }
        
        QTreeWidget::item:selected {
            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                      stop: 0 #2196f3, stop: 1 #1976d2);
            color: white;
            border-radius: 4px;
        }
        
        QTreeWidget::branch:has-children:!has-siblings:closed,
        QTreeWidget::branch:closed:has-children:has-siblings {
            border-image: none;
            image: url();
        }
        
        QTreeWidget::branch:open:has-children:!has-siblings,
        QTreeWidget::branch:open:has-children:has-siblings {
            border-image: none;
            image: url();
        }
        
        QHeaderView::section {
            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                      stop: 0 #f8f9fa, stop: 1 #e9ecef);
            border: 1px solid #dee2e6;
            padding: 8px 12px;
            font-weight: 600;
            color: #495057;
        }
        
        /* Text Edit */
        QTextEdit {
            background: white;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            padding: 16px;
            font-size: 13px;
            line-height: 1.5;
        }
        
        /* Progress Bar */
        QProgressBar {
            background: #e9ecef;
            border: none;
            border-radius: 8px;
            height: 16px;
            text-align: center;
            font-weight: 600;
            color: #495057;
        }
        
        QProgressBar::chunk {
            background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,
                                      stop: 0 #28a745, stop: 1 #20c997);
            border-radius: 8px;
        }
        
        /* Buttons */
        QPushButton {
            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                      stop: 0 #007bff, stop: 1 #0056b3);
            border: none;
            border-radius: 6px;
            color: white;
            font-weight: 600;
            padding: 10px 20px;
            font-size: 13px;
        }
        
        QPushButton:hover {
            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                      stop: 0 #0056b3, stop: 1 #004085);
        }
        
        QPushButton:pressed {
            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                      stop: 0 #004085, stop: 1 #002752);
        }
        
        QPushButton:disabled {
            background: #6c757d;
            color: #adb5bd;
        }
        
        /* Menu Bar */
        QMenuBar {
            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                      stop: 0 #ffffff, stop: 1 #f8f9fa);
            border-bottom: 1px solid #dee2e6;
            padding: 4px;
            font-weight: 500;
        }
        
        QMenuBar::item {
            background: transparent;
            padding: 8px 12px;
            border-radius: 4px;
            margin: 2px;
        }
        
        QMenuBar::item:selected {
            background: #e3f2fd;
            color: #1976d2;
        }
        
        QMenu {
            background: white;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            padding: 8px;
        }
        
        QMenu::item {
            padding: 8px 16px;
            border-radius: 4px;
            margin: 2px;
        }
        
        QMenu::item:selected {
            background: #e3f2fd;
            color: #1976d2;
        }
        
        /* Status Bar */
        QStatusBar {
            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                      stop: 0 #f8f9fa, stop: 1 #e9ecef);
            border-top: 1px solid #dee2e6;
            padding: 8px;
            font-size: 12px;
            color: #6c757d;
        }
        
        /* Labels */
        QLabel {
            color: #212529;
        }
        
        #sectionTitle {
            font-size: 16px;
            font-weight: 600;
            color: #495057;
            padding: 8px 0;
        }
        
        /* Input Fields */
        QLineEdit {
            background: white;
            border: 2px solid #e9ecef;
            border-radius: 6px;
            padding: 8px 12px;
            font-size: 13px;
            color: #495057;
        }
        
        QLineEdit:focus {
            border-color: #007bff;
            outline: none;
        }
        
        /* Dialog */
        QDialog {
            background: white;
            border-radius: 12px;
        }
        
        /* Scrollbar */
        QScrollBar:vertical {
            background: #f8f9fa;
            width: 12px;
            border-radius: 6px;
            margin: 0;
        }
        
        QScrollBar::handle:vertical {
            background: #ced4da;
            border-radius: 6px;
            min-height: 20px;
            margin: 2px;
        }
        
        QScrollBar::handle:vertical:hover {
            background: #adb5bd;
        }
        
        QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
            height: 0;
        }
        """
        
    def create_header_section(self, layout):
        """Create modern header section"""
        header_widget = QWidget()
        header_widget.setObjectName("headerWidget")
        header_widget.setFixedHeight(80)
        
        header_layout = QHBoxLayout(header_widget)
        header_layout.setContentsMargins(20, 15, 20, 15)
        
        # Left side - Title and subtitle
        left_layout = QVBoxLayout()
        left_layout.setSpacing(2)
        
        title_label = QLabel("🗄️ Hash Storage")
        title_label.setObjectName("headerTitle")
        left_layout.addWidget(title_label)
        
        subtitle_label = QLabel("基于内容哈希去重的文件存储系统")
        subtitle_label.setObjectName("headerSubtitle")
        left_layout.addWidget(subtitle_label)
        
        header_layout.addLayout(left_layout)
        header_layout.addStretch()
        
        # Right side - Connection status
        self.connection_status_label = QLabel("🔴 未连接")
        self.connection_status_label.setObjectName("connectionStatus")
        header_layout.addWidget(self.connection_status_label)
        
        layout.addWidget(header_widget)
        
    def init_ui(self):
        """Initialize the user interface"""
        self.setWindowTitle("🗄️ Hash Storage Client")
        self.setGeometry(100, 100, 1400, 900)
        self.setMinimumSize(1000, 700)
        
        # Set modern application style
        self.setStyleSheet(self.get_modern_stylesheet())
        
        # Set window icon
        try:
            from PyQt5.QtGui import QIcon
            import os
            icon_path = os.path.join(os.path.dirname(__file__), 'gui_ico.ico')
            if os.path.exists(icon_path):
                self.setWindowIcon(QIcon(icon_path))
        except Exception:
            pass  # Ignore icon loading errors
        
        # Create menu bar
        self.create_menu_bar()
        
        # Create central widget
        central_widget = QWidget()
        central_widget.setObjectName("centralWidget")
        self.setCentralWidget(central_widget)
        
        # Create main layout with modern spacing
        main_layout = QVBoxLayout(central_widget)
        main_layout.setContentsMargins(15, 15, 15, 15)
        main_layout.setSpacing(15)
        
        # Add header section
        self.create_header_section(main_layout)
        
        # Create splitter with modern styling
        splitter = QSplitter(Qt.Horizontal)
        splitter.setObjectName("mainSplitter")
        splitter.setHandleWidth(8)
        main_layout.addWidget(splitter)
        
        # File tree area
        self.create_file_tree_area(splitter)
        
        # Info panel
        self.create_info_panel(splitter)
        
        # Status bar
        self.create_status_bar()
        
        # Set splitter ratio (65% : 35%)
        splitter.setSizes([910, 490])
        
        # Connect button signals
        self.quick_upload_btn.clicked.connect(self.upload_files)
        self.copy_info_btn.clicked.connect(self.copy_info_to_clipboard)
        self.export_info_btn.clicked.connect(self.export_info_to_file)
        
    def create_menu_bar(self):
        """Create menu bar"""
        menubar = self.menuBar()
        
        # Connection menu
        connection_menu = menubar.addMenu('🔗 连接')
        
        # Connect action
        connect_action = QAction('🟢 连接服务器', self)
        connect_action.setShortcut('Ctrl+C')
        connect_action.setStatusTip('连接到 MinIO 存储服务器')
        connect_action.triggered.connect(self.connect_to_storage)
        connection_menu.addAction(connect_action)
        
        # Disconnect action
        self.disconnect_action = QAction('🔴 断开连接', self)
        self.disconnect_action.setShortcut('Ctrl+D')
        self.disconnect_action.setStatusTip('断开与服务器的连接')
        self.disconnect_action.setEnabled(False)
        self.disconnect_action.triggered.connect(self.disconnect_from_storage)
        connection_menu.addAction(self.disconnect_action)
        
        connection_menu.addSeparator()
        
        # Refresh action
        self.refresh_action = QAction('🔄 刷新文件列表', self)
        self.refresh_action.setShortcut('F5')
        self.refresh_action.setStatusTip('刷新文件树显示')
        self.refresh_action.setEnabled(False)
        self.refresh_action.triggered.connect(self.refresh_file_tree)
        connection_menu.addAction(self.refresh_action)
        
        # File menu
        file_menu = menubar.addMenu('📁 文件')
        
        # Upload action
        self.upload_action = QAction('⬆️ 上传文件', self)
        self.upload_action.setShortcut('Ctrl+U')
        self.upload_action.setStatusTip('上传文件或文件夹到存储服务器')
        self.upload_action.setEnabled(False)
        self.upload_action.triggered.connect(self.upload_files)
        file_menu.addAction(self.upload_action)
        

        
        # View menu
        view_menu = menubar.addMenu('👁️ 视图')
        
        # Toggle info panel
        self.toggle_info_action = QAction('📊 切换信息面板', self)
        self.toggle_info_action.setShortcut('F9')
        self.toggle_info_action.setStatusTip('显示/隐藏详细信息面板')
        self.toggle_info_action.setCheckable(True)
        self.toggle_info_action.setChecked(True)
        self.toggle_info_action.triggered.connect(self.toggle_info_panel)
        view_menu.addAction(self.toggle_info_action)
        
        # Expand all
        expand_all_action = QAction('📂 展开所有', self)
        expand_all_action.setShortcut('Ctrl+E')
        expand_all_action.setStatusTip('展开文件树中的所有目录')
        expand_all_action.triggered.connect(self.expand_all_items)
        view_menu.addAction(expand_all_action)
        
        # Collapse all
        collapse_all_action = QAction('📁 折叠所有', self)
        collapse_all_action.setShortcut('Ctrl+Shift+E')
        collapse_all_action.setStatusTip('折叠文件树中的所有目录')
        collapse_all_action.triggered.connect(self.collapse_all_items)
        view_menu.addAction(collapse_all_action)
        
        # Tools menu
        tools_menu = menubar.addMenu('🔧 工具')
        
        # Storage statistics
        stats_action = QAction('📈 存储统计', self)
        stats_action.setShortcut('Ctrl+I')
        stats_action.setStatusTip('查看存储空间使用统计')
        stats_action.triggered.connect(self.show_storage_stats)
        tools_menu.addAction(stats_action)
        

        
        tools_menu.addSeparator()
        

        
        # Help menu
        help_menu = menubar.addMenu('❓ 帮助')
        
        # User guide
        guide_action = QAction('📖 用户指南', self)
        guide_action.setShortcut('F1')
        guide_action.setStatusTip('查看用户使用指南')
        guide_action.triggered.connect(self.show_user_guide)
        help_menu.addAction(guide_action)
        
        help_menu.addSeparator()
        
        # About action
        about_action = QAction('ℹ️ 关于', self)
        about_action.setStatusTip('关于 Hash Storage 应用程序')
        about_action.triggered.connect(self.show_about_dialog)
        help_menu.addAction(about_action)
        
    def show_about_dialog(self):
        """Show about dialog"""
        import subprocess
        import os
        
        dialog = QDialog(self)
        dialog.setWindowTitle("About Hash Storage")
        dialog.setModal(True)
        dialog.resize(350, 200)
        dialog.setFixedSize(350, 200)
        
        layout = QVBoxLayout(dialog)
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # Title
        title_label = QLabel("Hash Storage")
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setStyleSheet("font-size: 18px; font-weight: bold; color: #2c3e50;")
        layout.addWidget(title_label)
        
        # Description
        desc_label = QLabel("A file storage system based on content hashing")
        desc_label.setAlignment(Qt.AlignCenter)
        desc_label.setStyleSheet("font-size: 12px; color: #7f8c8d; margin-bottom: 10px;")
        desc_label.setWordWrap(True)
        layout.addWidget(desc_label)
        
        # Author info
        author_label = QLabel(f"Author: Chenhu & Claude-4-Sonnet")
        author_label.setAlignment(Qt.AlignCenter)
        author_label.setStyleSheet("font-size: 14px; color: #34495e;")
        layout.addWidget(author_label)
        
        # Version info
        version_label = QLabel(f"Version: v0.1.0")
        version_label.setAlignment(Qt.AlignCenter)
        version_label.setStyleSheet("font-size: 14px; color: #34495e;")
        layout.addWidget(version_label)
        
        # Spacer
        layout.addStretch()
        
        # OK button
        button_layout = QHBoxLayout()
        button_layout.addStretch()
        ok_button = QPushButton("OK")
        ok_button.setFixedSize(80, 30)
        ok_button.clicked.connect(dialog.accept)
        button_layout.addWidget(ok_button)
        button_layout.addStretch()
        layout.addLayout(button_layout)
        
        dialog.exec_()
    
    def show_connection_dialog(self):
        """Show connection configuration dialog"""
        dialog = QDialog(self)
        dialog.setWindowTitle("Connection Configuration")
        dialog.setModal(True)
        dialog.resize(400, 200)
        
        layout = QFormLayout(dialog)
        
        # URL input
        url_input = QLineEdit()
        url_input.setPlaceholderText("http://localhost:9000")
        layout.addRow("URL:", url_input)
        
        # Access Key input
        access_key_input = QLineEdit()
        access_key_input.setPlaceholderText("minioadmin")
        layout.addRow("Access Key:", access_key_input)
        
        # Secret Key input
        secret_key_input = QLineEdit()
        secret_key_input.setPlaceholderText("minioadmin")
        secret_key_input.setEchoMode(QLineEdit.Password)
        layout.addRow("Secret Key:", secret_key_input)
        
        # Bucket input
        bucket_input = QLineEdit()
        bucket_input.setPlaceholderText("hash-storage")
        layout.addRow("Bucket:", bucket_input)
        
        # Load default values from environment
        try:
            from dotenv import load_dotenv
            load_dotenv()
            
            url_input.setText(os.getenv('URL', ''))
            access_key_input.setText(os.getenv('ACCESS_KEY', ''))
            bucket_input.setText(os.getenv('BUCKET', ''))
        except Exception:
            pass
        
        # Buttons
        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.accepted.connect(dialog.accept)
        button_box.rejected.connect(dialog.reject)
        layout.addRow(button_box)
        
        if dialog.exec_() == QDialog.Accepted:
            return {
                'url': url_input.text().strip(),
                'access_key': access_key_input.text().strip(),
                'secret_key': secret_key_input.text().strip(),
                'bucket': bucket_input.text().strip()
            }
        return None
        
    def load_default_config(self):
        """Load default configuration from environment variables"""
        try:
            from dotenv import load_dotenv
            load_dotenv()
            
            url = os.getenv('URL', '')
            access_key = os.getenv('ACCESS_KEY', '')
            bucket = os.getenv('BUCKET', '')
            
            if url:
                self.url_input.setText(url)
            if access_key:
                self.access_key_input.setText(access_key)
            if bucket:
                self.bucket_input.setText(bucket)
                
        except Exception:
            # Ignore errors when loading default config
            pass
        
    def create_file_tree_area(self, parent):
        """Create file tree area"""
        tree_widget = QWidget()
        tree_widget.setObjectName("treeWidget")
        tree_layout = QVBoxLayout(tree_widget)
        tree_layout.setContentsMargins(15, 15, 15, 15)
        tree_layout.setSpacing(12)
        
        # Header section with title and controls
        header_layout = QHBoxLayout()
        
        # Title with icon
        title_label = QLabel("📂 文件目录结构")
        title_label.setObjectName("sectionTitle")
        title_label.setFont(QFont("Segoe UI", 14, QFont.Bold))
        header_layout.addWidget(title_label)
        
        header_layout.addStretch()
        
        # Quick action buttons
        self.quick_upload_btn = QPushButton("⬆️ 快速上传")
        self.quick_upload_btn.setObjectName("quickActionBtn")
        self.quick_upload_btn.setFixedSize(150, 32)
        self.quick_upload_btn.setEnabled(False)
        self.quick_upload_btn.clicked.connect(self.upload_files)
        header_layout.addWidget(self.quick_upload_btn)
        

        
        tree_layout.addLayout(header_layout)
        
        # File tree with enhanced headers
        self.file_tree = QTreeWidget()
        self.file_tree.setHeaderLabels(["📄 名称", "📁 类型", "🔑 哈希", "📏 大小", "💾 缓存"])
        self.file_tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.file_tree.customContextMenuRequested.connect(self.show_context_menu)
        self.file_tree.setAlternatingRowColors(True)
        self.file_tree.setRootIsDecorated(True)
        self.file_tree.setAnimated(True)
        
        # Set column widths and properties
        header = self.file_tree.header()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.Fixed)
        header.setSectionResizeMode(2, QHeaderView.Fixed)
        header.setSectionResizeMode(3, QHeaderView.Fixed)
        header.setSectionResizeMode(4, QHeaderView.Fixed)
        
        # Set specific column widths
        header.resizeSection(1, 80)   # Type
        header.resizeSection(2, 120)  # Hash
        header.resizeSection(3, 100)  # Size
        header.resizeSection(4, 60)   # Cached
        
        tree_layout.addWidget(self.file_tree)
        
        # File count status
        self.file_count_label = QLabel("📊 文件总数: 0")
        self.file_count_label.setStyleSheet("color: #6c757d; font-size: 12px; padding: 5px;")
        tree_layout.addWidget(self.file_count_label)
        
        parent.addWidget(tree_widget)
        
    def create_info_panel(self, parent):
        """Create info panel"""
        self.info_widget = QWidget()
        self.info_widget.setObjectName("infoWidget")
        info_layout = QVBoxLayout(self.info_widget)
        info_layout.setContentsMargins(15, 15, 15, 15)
        info_layout.setSpacing(12)
        
        # Header section with title and controls
        header_layout = QHBoxLayout()
        
        # Title with icon
        title_label = QLabel("📊 详细信息")
        title_label.setObjectName("sectionTitle")
        title_label.setFont(QFont("Segoe UI", 14, QFont.Bold))
        header_layout.addWidget(title_label)
        
        header_layout.addStretch()
        
        # Quick action buttons for info panel
        self.copy_info_btn = QPushButton("📋 复制信息")
        self.copy_info_btn.setObjectName("quickActionBtn")
        self.copy_info_btn.setFixedSize(120, 36)
        self.copy_info_btn.setEnabled(False)
        self.copy_info_btn.clicked.connect(self.copy_info_to_clipboard)
        header_layout.addWidget(self.copy_info_btn)
        
        self.export_info_btn = QPushButton("💾 导出")
        self.export_info_btn.setObjectName("quickActionBtn")
        self.export_info_btn.setFixedSize(100, 36)
        self.export_info_btn.setEnabled(False)
        self.export_info_btn.clicked.connect(self.export_info_to_file)
        header_layout.addWidget(self.export_info_btn)
        
        info_layout.addLayout(header_layout)
        
        # Info display area with modern styling
        self.info_text = QTextEdit()
        self.info_text.setHtml("""
        <div style='padding: 30px; font-family: "Segoe UI", "SF Pro Display", "Helvetica Neue", Arial, sans-serif; 
                    color: #8e8e93; text-align: center; line-height: 1.6;'>
            <div style='font-size: 48px; margin-bottom: 20px;'>📂</div>
            <h3 style='color: #007AFF; margin: 0 0 10px 0; font-weight: 600;'>欢迎使用 Hash Storage</h3>
            <p style='margin: 0; font-size: 14px;'>选择左侧文件树中的文件或目录<br/>查看详细信息和操作选项</p>
            <div style='margin-top: 20px; padding: 15px; background: #f0f8ff; border-radius: 8px; border-left: 4px solid #007AFF;'>
                <p style='margin: 0; font-size: 13px; color: #495057;'>
                    💡 <strong>提示:</strong> 右键点击文件可进行下载、重命名、删除等操作
                </p>
            </div>
        </div>
        """)
        self.info_text.setReadOnly(True)
        self.info_text.setWordWrapMode(QTextOption.WordWrap)
        info_layout.addWidget(self.info_text)
        
        # Progress section
        progress_widget = QWidget()
        progress_layout = QVBoxLayout(progress_widget)
        progress_layout.setContentsMargins(0, 10, 0, 0)
        progress_layout.setSpacing(8)
        
        # Progress label
        self.progress_label = QLabel("")
        self.progress_label.setStyleSheet("color: #495057; font-weight: 500; font-size: 13px;")
        self.progress_label.setVisible(False)
        progress_layout.addWidget(self.progress_label)
        
        # Progress bar with modern styling
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        self.progress_bar.setFixedHeight(20)
        progress_layout.addWidget(self.progress_bar)
        
        # Speed and ETA labels
        speed_eta_layout = QHBoxLayout()
        self.speed_label = QLabel("")
        self.speed_label.setStyleSheet("color: #6c757d; font-size: 12px;")
        self.speed_label.setVisible(False)
        speed_eta_layout.addWidget(self.speed_label)
        
        speed_eta_layout.addStretch()
        
        self.eta_label = QLabel("")
        self.eta_label.setStyleSheet("color: #6c757d; font-size: 12px;")
        self.eta_label.setVisible(False)
        speed_eta_layout.addWidget(self.eta_label)
        
        progress_layout.addLayout(speed_eta_layout)
        info_layout.addWidget(progress_widget)
        
        parent.addWidget(self.info_widget)
        
    def create_status_bar(self):
        """Create status bar"""
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.show_status_message("Ready")
        
    def show_status_message(self, message):
        """Show status message with timestamp"""
        from datetime import datetime
        timestamp = datetime.now().strftime("%H:%M:%S")
        formatted_message = f"[{timestamp}] {message}"
        self.status_bar.showMessage(formatted_message)
        
    def setup_connections(self):
        """Setup signal connections"""
        self.file_tree.itemClicked.connect(self.on_item_clicked)
        
    def connect_to_storage(self):
        """Connect to storage"""
        try:
            # Show connection dialog
            config = self.show_connection_dialog()
            if not config:
                return  # User cancelled
            
            # Create HashStorage instance with custom config or env variables
            if config['url'] and config['access_key'] and config['secret_key'] and config['bucket']:
                self.storage = HashStorage(
                    access_key=config['access_key'],
                    secret_key=config['secret_key'],
                    url=config['url'],
                    bucket=config['bucket']
                )
                # Update window title with server info
                self.setWindowTitle(f"Hash Storage - {config['url']} ({config['bucket']})")
            else:
                # Use environment variables if inputs are empty
                self.storage = HashStorage()
                # Try to get info for title
                try:
                    from dotenv import load_dotenv
                    load_dotenv()
                    url = os.getenv('URL', 'localhost')
                    bucket = os.getenv('BUCKET', 'hash-storage')
                    self.setWindowTitle(f"Hash Storage - {url} ({bucket})")
                except Exception:
                    self.setWindowTitle("Hash Storage - Connected")
            
            # Update menu states
            self.disconnect_action.setEnabled(True)
            self.refresh_action.setEnabled(True)
            self.upload_action.setEnabled(True)
            self.quick_upload_btn.setEnabled(True)
            
            # Update connection status
            self.connection_status_label.setText("🟢 已连接")
            self.show_status_message("✅ 连接成功 - 服务器响应正常")
            
            # Auto refresh file tree
            self.refresh_file_tree()
            
        except Exception as e:
            QMessageBox.critical(self, "Connection Error", f"Unable to connect to MinIO server:\n{str(e)}")
            
    def disconnect_from_storage(self):
        """Disconnect from storage"""
        self.storage = None
        self.meta_data = {}
        self.file_tree.clear()
        
        # Reset info panel to welcome message
        self.info_text.setHtml("""
        <div style='padding: 30px; font-family: "Segoe UI", "SF Pro Display", "Helvetica Neue", Arial, sans-serif; 
                    color: #8e8e93; text-align: center; line-height: 1.6;'>
            <div style='font-size: 48px; margin-bottom: 20px;'>📂</div>
            <h3 style='color: #007AFF; margin: 0 0 10px 0; font-weight: 600;'>欢迎使用 Hash Storage</h3>
            <p style='margin: 0; font-size: 14px;'>选择左侧文件树中的文件或目录<br/>查看详细信息和操作选项</p>
            <div style='margin-top: 20px; padding: 15px; background: #f0f8ff; border-radius: 8px; border-left: 4px solid #007AFF;'>
                <p style='margin: 0; font-size: 13px; color: #495057;'>
                    💡 <strong>提示:</strong> 右键点击文件可进行下载、重命名、删除等操作
                </p>
            </div>
        </div>
        """)
        
        # Reset window title
        self.setWindowTitle("🗄️ Hash Storage Client")
        
        # Update menu states
        self.disconnect_action.setEnabled(False)
        self.refresh_action.setEnabled(False)
        self.upload_action.setEnabled(False)
        self.quick_upload_btn.setEnabled(False)
        self.copy_info_btn.setEnabled(False)
        self.export_info_btn.setEnabled(False)
        
        # Update connection status
        self.connection_status_label.setText("🔴 未连接")
        self.file_count_label.setText("📊 文件总数: 0")
        self.show_status_message("🔌 已断开连接 - 请重新连接服务器")
            
    def refresh_file_tree(self):
        """Refresh file tree"""
        if not self.storage:
            return
            
        try:
            self.show_status_message("🔄 正在加载文件列表...")
            self.meta_data = self.storage._get_meta_data()
            self.build_file_tree()
            
            # Update file count
            total_files = len(self.meta_data.get('file_to_hash', {}))
            self.file_count_label.setText(f"📊 文件总数: {total_files:,}")
            
            self.show_status_message(f"✅ 已加载 {total_files:,} 个文件")
            
        except Exception as e:
            QMessageBox.critical(self, "刷新错误", f"无法刷新文件列表:\n{str(e)}")
            self.show_status_message("❌ 刷新失败")
            
    def build_file_tree(self):
        """Build file tree"""
        self.file_tree.clear()
        
        if not self.meta_data or 'file_to_hash' not in self.meta_data:
            return
            
        # Build directory structure
        root_items = {}
        
        for file_path, file_hash in self.meta_data['file_to_hash'].items():
            parts = file_path.split('/')
            current_level = root_items
            current_path = ""
            
            # Create directory structure
            for i, part in enumerate(parts):
                if current_path:
                    current_path += "/" + part
                else:
                    current_path = part
                    
                if i == len(parts) - 1:  # File
                    # Get file info
                    file_info = self.storage.get_file_info(file_path) if self.storage else None
                    if file_info:
                        size_str = self.storage._format_file_size(file_info['size'])
                        cache_str = "✓" if file_info['has_cache'] else "✗"
                    else:
                        size_str = "--"
                        cache_str = "--"
                    
                    item = QTreeWidgetItem([part, "File", file_hash[:8], size_str, cache_str])
                    item.setData(0, Qt.UserRole, {
                        'type': 'file',
                        'path': file_path,
                        'hash': file_hash
                    })
                    current_level[part] = item
                else:  # Directory
                    if part not in current_level:
                        item = QTreeWidgetItem([part, "Directory", "--", "--", "--"])
                        item.setData(0, Qt.UserRole, {
                            'type': 'directory',
                            'path': current_path
                        })
                        current_level[part] = {'_item': item, '_children': {}}
                    current_level = current_level[part]['_children']
        
        # Add to tree
        self._add_items_to_tree(root_items, self.file_tree)
        
        # Expand first level directories
        self.file_tree.expandToDepth(0)
        
    def _add_items_to_tree(self, items_dict, parent):
        """Recursively add items to tree"""
        for name, value in sorted(items_dict.items()):
            if isinstance(value, dict) and '_item' in value:
                # 这是一个目录
                item = value['_item']
                if hasattr(parent, 'addTopLevelItem'):
                    parent.addTopLevelItem(item)
                else:
                    parent.addChild(item)
                
                # Recursively add child items
                if '_children' in value:
                    self._add_items_to_tree(value['_children'], item)
            else:
                # This is a file
                item = value
                if hasattr(parent, 'addTopLevelItem'):
                    parent.addTopLevelItem(item)
                else:
                    parent.addChild(item)
                
    def on_item_clicked(self, item, column):
        """Handle item click events"""
        data = item.data(0, Qt.UserRole)
        if not data:
            return
            
        if data['type'] == 'file':
            # Get detailed file information
            file_info = self.storage.get_file_info(data['path']) if self.storage else None
            if file_info:
                size_str = self.storage._format_file_size(file_info['size'])
                cache_status = "✅ Cached" if file_info['has_cache'] else "❌ Not Cached"
                cache_icon = "💾" if file_info['has_cache'] else "🔄"
            else:
                size_str = "❓ Unknown"
                cache_status = "❓ Unknown"
                cache_icon = "❓"
            
            # Get file extension for icon
            file_ext = data['path'].split('.')[-1].lower() if '.' in data['path'] else ''
            file_icon = self._get_file_icon(file_ext)
            
            info_text = f"""<div style='font-family: "Segoe UI", "SF Pro Display", "Helvetica Neue", Arial, sans-serif; line-height: 1.6; color: #1d1d1f; background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); padding: 25px; border-radius: 16px;'>
<div style='text-align: center; margin-bottom: 25px; background: white; padding: 20px; border-radius: 12px;'>
<div style='font-size: 32px; margin-bottom: 10px;'>{file_icon}</div>
<h2 style='color: #007AFF; margin: 0; font-size: 20px; font-weight: 700;'>文件详细信息</h2>
<p style='color: #6c757d; margin: 5px 0 0 0; font-size: 14px;'>File Information</p>
</div>

<div style='display: grid; gap: 16px;'>
<div style='background: linear-gradient(135deg, #e3f2fd 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #2196F3;'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>📄</span><strong style='color: #1565C0; font-size: 15px;'>文件名</strong></div>
<div style='margin-left: 28px; font-family: "SF Mono", "Cascadia Code", Consolas, monospace; font-size: 13px; word-break: break-all; color: #424242; background: rgba(33,150,243,0.05); padding: 8px 12px; border-radius: 6px;'>{data['path']}</div>
</div>

<div style='background: linear-gradient(135deg, #e8f5e8 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #4CAF50;'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>📁</span><strong style='color: #2E7D32; font-size: 15px;'>文件类型</strong></div>
<div style='margin-left: 28px; color: #4CAF50; font-weight: 600; font-size: 16px;'>文件</div>
</div>

<div style='background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #FF9800;'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>🔑</span><strong style='color: #E65100; font-size: 15px;'>哈希值 (前16位)</strong></div>
<div style='margin-left: 28px; color: #FF9800; font-family: "SF Mono", "Cascadia Code", Consolas, monospace; font-size: 14px; font-weight: 600; background: rgba(255,152,0,0.1); padding: 8px 12px; border-radius: 6px;'>{data['hash'][:16]}...</div>
</div>

<div style='background: linear-gradient(135deg, #f3e5f5 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #9C27B0;'>
<div style='display: flex; align-items: center; margin-bottom: 12px;'><span style='font-size: 18px; margin-right: 10px;'>🔐</span><strong style='color: #6A1B9A; font-size: 15px;'>完整哈希值</strong></div>
<div style='margin-left: 28px; color: #8e8e93; font-family: "SF Mono", "Cascadia Code", Consolas, monospace; font-size: 11px; word-break: break-all; line-height: 1.5; background: rgba(156,39,176,0.05); padding: 10px 12px; border-radius: 6px; border: 1px solid rgba(156,39,176,0.1);'>{data['hash']}</div>
</div>

<div style='background: linear-gradient(135deg, #fff8e1 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #FFC107;'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>📏</span><strong style='color: #F57F17; font-size: 15px;'>文件大小</strong></div>
<div style='margin-left: 28px; color: #FF8F00; font-weight: 600; font-size: 16px;'>{size_str}</div>
</div>

<div style='background: linear-gradient(135deg, {"#e8f5e8" if "✅" in cache_status else "#ffebee"} 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid {"#4CAF50" if "✅" in cache_status else "#f44336"};'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>{cache_icon}</span><strong style='color: {"#2E7D32" if "✅" in cache_status else "#C62828"}; font-size: 15px;'>缓存状态</strong></div>
<div style='margin-left: 28px; color: {"#4CAF50" if "✅" in cache_status else "#f44336"}; font-weight: 600; font-size: 16px;'>{cache_status}</div>
</div>
</div>

<div style='margin-top: 25px; background: white; border-radius: 12px; overflow: hidden;'>
<div style='background: linear-gradient(135deg, #007AFF 0%, #0056CC 100%); color: white; padding: 15px; text-align: center;'>
<h3 style='margin: 0; font-size: 16px; font-weight: 600; display: flex; align-items: center; justify-content: center;'>
<span style='font-size: 20px; margin-right: 8px;'>🔗</span> 相同内容的文件
</h3>
</div>
<div style='padding: 20px;'>
"""
            # Find files with same hash
            same_files = self.meta_data.get('hash_to_files', {}).get(data['hash'], [])
            if same_files:
                for i, f in enumerate(same_files):
                    file_ext = f.split('.')[-1].lower() if '.' in f else ''
                    f_icon = self._get_file_icon(file_ext)
                    border_style = "border-bottom: 1px solid #f0f0f0;" if i < len(same_files) - 1 else ""
                    info_text += f"<div style='padding: 15px 0; {border_style} display: flex; align-items: center; transition: background-color 0.2s;' onmouseover='this.style.backgroundColor=\"#f8f9fa\"' onmouseout='this.style.backgroundColor=\"transparent\"'><span style='font-size: 18px; margin-right: 12px;'>{f_icon}</span><span style='font-family: \"SF Mono\", \"Cascadia Code\", Consolas, monospace; color: #495057; font-size: 13px; flex: 1;'>{f}</span></div>\n"
            else:
                info_text += "<div style='color: #6c757d; font-style: italic; padding: 20px 0; text-align: center; background: #f8f9fa; border-radius: 8px; margin: 10px 0;'><span style='font-size: 24px; display: block; margin-bottom: 8px;'>📭</span>没有找到相同内容的其他文件</div>\n"
            info_text += "</div></div></div>"
                
        else:  # Directory
            # Count files in directory and calculate total size
            file_count, total_size = self._get_directory_stats(data['path'])
            size_str = self.storage._format_file_size(total_size) if self.storage else "❓ Unknown"
            
            info_text = f"""<div style='font-family: "Segoe UI", "SF Pro Display", "Helvetica Neue", Arial, sans-serif; line-height: 1.6; color: #1d1d1f; background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); padding: 25px; border-radius: 16px;'>
<div style='text-align: center; margin-bottom: 25px; background: white; padding: 20px; border-radius: 12px;'>
<div style='font-size: 32px; margin-bottom: 10px;'>📁</div>
<h2 style='color: #007AFF; margin: 0; font-size: 20px; font-weight: 700;'>目录详细信息</h2>
<p style='color: #6c757d; margin: 5px 0 0 0; font-size: 14px;'>Directory Information</p>
</div>

<div style='display: grid; gap: 16px;'>
<div style='background: linear-gradient(135deg, #e3f2fd 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #2196F3;'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>📂</span><strong style='color: #1565C0; font-size: 15px;'>目录名</strong></div>
<div style='margin-left: 28px; font-family: "SF Mono", "Cascadia Code", Consolas, monospace; font-size: 13px; word-break: break-all; color: #424242; background: rgba(33,150,243,0.05); padding: 8px 12px; border-radius: 6px;'>{data['path']}</div>
</div>

<div style='background: linear-gradient(135deg, #e8f5e8 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #4CAF50;'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>📁</span><strong style='color: #2E7D32; font-size: 15px;'>类型</strong></div>
<div style='margin-left: 28px; color: #4CAF50; font-weight: 600; font-size: 16px;'>目录</div>
</div>

<div style='background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #FF9800;'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>📊</span><strong style='color: #E65100; font-size: 15px;'>包含文件</strong></div>
<div style='margin-left: 28px; color: #FF9800; font-weight: 600; font-size: 16px;'>{file_count:,} 个文件</div>
</div>

<div style='background: linear-gradient(135deg, #f3e5f5 0%, #ffffff 100%); padding: 18px; border-radius: 12px; border-left: 5px solid #9C27B0;'>
<div style='display: flex; align-items: center; margin-bottom: 8px;'><span style='font-size: 18px; margin-right: 10px;'>💾</span><strong style='color: #6A1B9A; font-size: 15px;'>总大小</strong></div>
<div style='margin-left: 28px; color: #9C27B0; font-weight: 600; font-size: 16px;'>{size_str}</div>
</div>
</div>

<div style='margin-top: 25px; background: white; border-radius: 12px; overflow: hidden;'>
<div style='background: linear-gradient(135deg, #007AFF 0%, #0056CC 100%); color: white; padding: 15px; text-align: center;'>
<h3 style='margin: 0; font-size: 16px; font-weight: 600; display: flex; align-items: center; justify-content: center;'>
<span style='font-size: 20px; margin-right: 8px;'>📋</span> 目录中的文件
</h3>
</div>
<div style='padding: 20px; max-height: 300px; overflow-y: auto;'>
"""
            # List files in directory
            files_in_dir = self._get_files_in_directory(data['path'])
            if files_in_dir:
                for i, file_path in enumerate(sorted(files_in_dir)):
                    file_name = file_path.split('/')[-1]
                    file_ext = file_name.split('.')[-1].lower() if '.' in file_name else ''
                    f_icon = self._get_file_icon(file_ext)
                    border_style = "border-bottom: 1px solid #f0f0f0;" if i < len(files_in_dir) - 1 else ""
                    
                    file_info = self.storage.get_file_info(file_path) if self.storage else None
                    if file_info:
                        file_size = self.storage._format_file_size(file_info['size'])
                        info_text += f"<div style='padding: 15px 0; {border_style} display: flex; justify-content: space-between; align-items: center; transition: background-color 0.2s;' onmouseover='this.style.backgroundColor=\"#f8f9fa\"' onmouseout='this.style.backgroundColor=\"transparent\"'><span style='display: flex; align-items: center;'><span style='font-size: 18px; margin-right: 12px;'>{f_icon}</span><span style='font-family: \"SF Mono\", \"Cascadia Code\", Consolas, monospace; color: #495057; font-size: 13px;'>{file_name}</span></span><span style='color: #6c757d; font-size: 12px; font-weight: 600; background: #e9ecef; padding: 4px 8px; border-radius: 4px;'>{file_size}</span></div>\n"
                    else:
                        info_text += f"<div style='padding: 15px 0; {border_style} display: flex; align-items: center; transition: background-color 0.2s;' onmouseover='this.style.backgroundColor=\"#f8f9fa\"' onmouseout='this.style.backgroundColor=\"transparent\"'><span style='font-size: 18px; margin-right: 12px;'>{f_icon}</span><span style='font-family: \"SF Mono\", \"Cascadia Code\", Consolas, monospace; color: #495057; font-size: 13px;'>{file_name}</span></div>\n"
            else:
                info_text += "<div style='color: #6c757d; font-style: italic; padding: 20px 0; text-align: center; background: #f8f9fa; border-radius: 8px; margin: 10px 0;'><span style='font-size: 24px; display: block; margin-bottom: 8px;'>📭</span>目录为空</div>\n"
            info_text += "</div></div></div>"
            
        self.info_text.setHtml(info_text)
        
        # Enable copy and export buttons when info is displayed
        self.copy_info_btn.setEnabled(True)
        self.export_info_btn.setEnabled(True)
        
    def _get_file_icon(self, file_ext):
        """Get file icon based on extension"""
        icon_map = {
            # Images
            'jpg': '🖼️', 'jpeg': '🖼️', 'png': '🖼️', 'gif': '🖼️', 'bmp': '🖼️', 'svg': '🖼️', 'webp': '🖼️',
            'ico': '🖼️', 'tiff': '🖼️', 'tif': '🖼️',
            
            # Documents
            'pdf': '📄', 'doc': '📝', 'docx': '📝', 'txt': '📄', 'rtf': '📝', 'odt': '📝',
            'xls': '📊', 'xlsx': '📊', 'csv': '📊', 'ods': '📊',
            'ppt': '📊', 'pptx': '📊', 'odp': '📊',
            
            # Code files
            'py': '🐍', 'js': '💛', 'html': '🌐', 'css': '🎨', 'php': '🐘', 'java': '☕',
            'cpp': '⚙️', 'c': '⚙️', 'h': '⚙️', 'hpp': '⚙️',
            'go': '🐹', 'rs': '🦀', 'swift': '🐦', 'kt': '🟣',
            'rb': '💎', 'pl': '🐪', 'sh': '🐚', 'bat': '⚡', 'ps1': '💙',
            'sql': '🗃️', 'json': '📋', 'xml': '📋', 'yaml': '📋', 'yml': '📋',
            
            # Archives
            'zip': '🗜️', 'rar': '🗜️', '7z': '🗜️', 'tar': '🗜️', 'gz': '🗜️', 'bz2': '🗜️',
            'xz': '🗜️', 'dmg': '💿', 'iso': '💿',
            
            # Audio
            'mp3': '🎵', 'wav': '🎵', 'flac': '🎵', 'aac': '🎵', 'ogg': '🎵', 'm4a': '🎵',
            'wma': '🎵', 'opus': '🎵',
            
            # Video
            'mp4': '🎬', 'avi': '🎬', 'mkv': '🎬', 'mov': '🎬', 'wmv': '🎬', 'flv': '🎬',
            'webm': '🎬', 'm4v': '🎬', '3gp': '🎬',
            
            # Executables
            'exe': '⚙️', 'msi': '⚙️', 'deb': '📦', 'rpm': '📦', 'dmg': '💿', 'pkg': '📦',
            'app': '📱', 'apk': '📱',
            
            # Fonts
            'ttf': '🔤', 'otf': '🔤', 'woff': '🔤', 'woff2': '🔤', 'eot': '🔤',
            
            # Others
            'log': '📋', 'md': '📝', 'readme': '📖', 'license': '📜', 'gitignore': '🙈',
            'config': '⚙️', 'conf': '⚙️', 'ini': '⚙️', 'cfg': '⚙️',
        }
        
        return icon_map.get(file_ext, '📄')  # Default to document icon
        
    def _count_files_in_directory(self, dir_path):
        """Count files in directory"""
        count = 0
        dir_prefix = dir_path.rstrip('/') + '/'
        
        for file_path in self.meta_data.get('file_to_hash', {}).keys():
            if file_path.startswith(dir_prefix):
                count += 1
                
        return count
        
    def _get_directory_stats(self, dir_path):
        """Get directory statistics (file count and total size)"""
        count = 0
        total_size = 0
        dir_prefix = dir_path.rstrip('/') + '/'
        
        for file_path in self.meta_data.get('file_to_hash', {}).keys():
            if file_path.startswith(dir_prefix):
                count += 1
                # Get file size
                if self.storage:
                    file_info = self.storage.get_file_info(file_path)
                    if file_info:
                        total_size += file_info['size']
                        
        return count, total_size
        
    def _get_files_in_directory(self, dir_path):
        """Get list of files in directory"""
        files = []
        dir_prefix = dir_path.rstrip('/') + '/'
        
        for file_path in self.meta_data.get('file_to_hash', {}).keys():
            if file_path.startswith(dir_prefix):
                # Only include direct children, not subdirectory files
                relative_path = file_path[len(dir_prefix):]
                if '/' not in relative_path:  # Direct child file
                    files.append(file_path)
                    
        return files
        
    def show_context_menu(self, position):
        """Show context menu"""
        item = self.file_tree.itemAt(position)
        if not item:
            return
            
        data = item.data(0, Qt.UserRole)
        if not data:
            return
            
        menu = QMenu(self)
        
        if data['type'] == 'file':
            download_action = QAction("Download File", self)
            download_action.triggered.connect(lambda: self.download_item(data['path']))
            menu.addAction(download_action)
            
            rename_action = QAction("Rename File", self)
            rename_action.triggered.connect(lambda: self.rename_item(data['path'], 'file'))
            menu.addAction(rename_action)
            
            delete_action = QAction("Delete File", self)
            delete_action.triggered.connect(lambda: self.delete_item(data['path'], 'file'))
            menu.addAction(delete_action)
        else:  # Directory
            download_action = QAction("Download Directory", self)
            download_action.triggered.connect(lambda: self.download_item(data['path']))
            menu.addAction(download_action)
            
            rename_action = QAction("Rename Directory", self)
            rename_action.triggered.connect(lambda: self.rename_item(data['path'], 'directory'))
            menu.addAction(rename_action)
            
            delete_action = QAction("Delete Directory", self)
            delete_action.triggered.connect(lambda: self.delete_item(data['path'], 'directory'))
            menu.addAction(delete_action)
            
        menu.addSeparator()
        
        # View detailed information
        info_action = QAction("View Details", self)
        info_action.triggered.connect(lambda: self.on_item_clicked(item, 0))
        menu.addAction(info_action)
        
        menu.exec_(self.file_tree.mapToGlobal(position))
        
    def upload_files(self):
        """Upload files or folders"""
        if not self.storage:
            QMessageBox.warning(self, "Warning", "Please connect to storage server first")
            return
            
        # Select upload type
        msg_box = QMessageBox()
        msg_box.setWindowTitle("Select Upload Type")
        msg_box.setText("Please select the type to upload:")
        file_btn = msg_box.addButton("File", QMessageBox.YesRole)
        folder_btn = msg_box.addButton("Folder", QMessageBox.NoRole)
        cancel_btn = msg_box.addButton("Cancel", QMessageBox.RejectRole)
        msg_box.setDefaultButton(cancel_btn)
        
        msg_box.exec_()
        clicked_btn = msg_box.clickedButton()
        
        if clicked_btn == cancel_btn:
            return
            
        file_path = None
        is_directory = False
        
        if clicked_btn == file_btn:  # Upload file
            file_path, _ = QFileDialog.getOpenFileName(
                self, "Select file to upload", str(Path.home())
            )
        elif clicked_btn == folder_btn:  # Upload folder
            file_path = QFileDialog.getExistingDirectory(
                self, "Select folder to upload", str(Path.home())
            )
            is_directory = True
            
        if not file_path:
            return
            
        # Ask for custom name
        from PyQt5.QtWidgets import QInputDialog
        default_name = Path(file_path).name
        custom_name, ok = QInputDialog.getText(
            self, "Custom Name", 
            f"Enter custom name (leave empty to use default name '{default_name}'):",
            text=default_name
        )
        
        if not ok:
            return
            
        if not custom_name.strip():
            custom_name = None
        else:
            custom_name = custom_name.strip()
            
        # Start upload
        self.start_upload(file_path, custom_name)
        
    def start_upload(self, file_path, custom_name):
        """Start upload"""
        if self.upload_worker and self.upload_worker.isRunning():
            QMessageBox.information(self, "Info", "Upload task is already in progress, please wait")
            return
            
        self.upload_worker = UploadWorker(self.storage, file_path, custom_name)
        self.upload_worker.progress.connect(self.on_upload_progress)
        self.upload_worker.progress_value.connect(self.on_upload_progress_value)
        self.upload_worker.finished.connect(self.on_upload_finished)
        
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 100)  # Percentage-based progress
        self.progress_bar.setValue(0)
        self.upload_worker.start()
        
    def on_upload_progress(self, message):
        """Upload progress update"""
        self.show_status_message(message)
        
    def on_upload_progress_value(self, percentage):
        """Upload progress value update"""
        self.progress_bar.setValue(percentage)
        # Update status with percentage
        if percentage < 100:
            self.show_status_message(f"Uploading... {percentage}%")
        
    def on_upload_finished(self, success, message):
        """Upload completed"""
        self.progress_bar.setVisible(False)
        self.show_status_message(message)
        
        if success:
            QMessageBox.information(self, "Upload Complete", message)
            # Auto refresh file tree
            self.refresh_file_tree()
        else:
            QMessageBox.critical(self, "Upload Failed", message)
    
    def rename_item(self, item_path: str, item_type: str):
        """Rename file or directory"""
        if not self.storage:
            QMessageBox.warning(self, "Warning", "Please connect to storage server first")
            return
        
        from PyQt5.QtWidgets import QInputDialog
        
        # Get current name
        if item_type == 'file':
            current_name = item_path
            dialog_title = "Rename File"
            prompt_text = f"Enter new file name:"
        else:
            current_name = item_path
            dialog_title = "Rename Directory"
            prompt_text = f"Enter new directory name:"
        
        # Show input dialog
        new_name, ok = QInputDialog.getText(
            self, dialog_title, prompt_text, text=current_name
        )
        
        if not ok or not new_name.strip():
            return
        
        new_name = new_name.strip()
        if new_name == current_name:
            return
        
        # Execute rename
        try:
            if item_type == 'file':
                success = self.storage.rename_file(current_name, new_name)
            else:
                success = self.storage.rename_directory(current_name, new_name)
            
            if success:
                QMessageBox.information(self, "Rename Successful", f"Successfully renamed to: {new_name}")
                # Refresh file tree
                self.refresh_file_tree()
            else:
                QMessageBox.critical(self, "Rename Failed", "Rename operation failed, please check for name conflicts")
                
        except Exception as e:
            QMessageBox.critical(self, "Rename Failed", f"Error occurred during rename: {str(e)}")
    
    def delete_item(self, item_path: str, item_type: str):
        """Delete file or directory"""
        if not self.storage:
            QMessageBox.warning(self, "Warning", "Please connect to storage server first")
            return
        
        # Confirm deletion
        if item_type == 'file':
            message = f"Are you sure you want to delete file '{item_path}'?\n\nNote: This will only delete metadata, not the actual file content."
            title = "Confirm File Deletion"
        else:
            message = f"Are you sure you want to delete directory '{item_path}' and all its contents?\n\nNote: This will only delete metadata, not the actual file content."
            title = "Confirm Directory Deletion"
        
        reply = QMessageBox.question(
            self, title, message,
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )
        
        if reply != QMessageBox.Yes:
            return
        
        # Execute deletion
        try:
            if item_type == 'file':
                success = self.storage.delete_file(item_path)
            else:
                success = self.storage.delete_directory(item_path)
            
            if success:
                QMessageBox.information(self, "Delete Successful", f"Successfully deleted: {item_path}")
                # Refresh file tree
                self.refresh_file_tree()
            else:
                QMessageBox.critical(self, "Delete Failed", "Delete operation failed")
                
        except Exception as e:
            QMessageBox.critical(self, "Delete Failed", f"Error occurred during deletion: {str(e)}")
        
    def download_item(self, item_path):
        """Download item"""
        if not self.storage:
            QMessageBox.warning(self, "Warning", "Please connect to storage server first")
            return
            
        # Select download directory
        download_dir = QFileDialog.getExistingDirectory(
            self, "Select Download Directory", str(Path.home() / "Downloads")
        )
        
        if not download_dir:
            return
            
        # Determine output path
        item_name = item_path.split('/')[-1] if '/' in item_path else item_path
        output_path = Path(download_dir) / item_name
        
        # Start download
        self.start_download(item_path, str(output_path))
        
    def start_download(self, filename, output_path):
        """Start download"""
        if self.download_worker and self.download_worker.isRunning():
            QMessageBox.information(self, "Info", "Download task is already in progress, please wait")
            return
            
        self.download_worker = DownloadWorker(self.storage, filename, output_path)
        self.download_worker.progress.connect(self.on_download_progress)
        self.download_worker.progress_value.connect(self.on_download_progress_value)
        self.download_worker.finished.connect(self.on_download_finished)
        
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 100)  # Percentage-based progress
        self.progress_bar.setValue(0)
        self.download_worker.start()
        
    def on_download_progress(self, message):
        """Download progress update"""
        self.show_status_message(message)
        
    def on_download_progress_value(self, percentage):
        """Download progress value update"""
        self.progress_bar.setValue(percentage)
        # Update status with percentage
        if percentage < 100:
            self.show_status_message(f"Downloading... {percentage}%")
        
    def on_download_finished(self, success, message):
        """Download completed"""
        self.progress_bar.setVisible(False)
        self.show_status_message(message)
        
        if success:
            QMessageBox.information(self, "Download Complete", message)
        else:
            QMessageBox.critical(self, "Download Failed", message)
            
    def closeEvent(self, event):
        """Window close event"""
        if self.download_worker and self.download_worker.isRunning():
            reply = QMessageBox.question(
                self, "Confirm Exit", 
                "Download task is in progress, are you sure you want to exit?",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No
            )
            
            if reply == QMessageBox.Yes:
                self.download_worker.terminate()
                self.download_worker.wait()
                event.accept()
            else:
                event.ignore()
        else:
            event.accept()



    
    def toggle_info_panel(self):
        """Toggle info panel visibility"""
        if self.info_widget.isVisible():
            self.info_widget.hide()
        else:
            self.info_widget.show()
    
    def expand_all_items(self):
        """Expand all items in file tree"""
        self.file_tree.expandAll()
        self.show_status_message("📂 已展开所有目录")
    
    def collapse_all_items(self):
        """Collapse all items in file tree"""
        self.file_tree.collapseAll()
        self.show_status_message("📁 已折叠所有目录")
    
    def show_storage_stats(self):
        """Show storage statistics"""
        if not self.storage:
            QMessageBox.warning(self, "统计信息", "请先连接到存储服务器")
            return
        
        total_files = len(self.meta_data.get('file_to_hash', {}))
        total_size = sum(self.storage.get_file_info(path).get('size', 0) 
                        for path in self.meta_data.get('file_to_hash', {}).keys() 
                        if self.storage.get_file_info(path))
        size_str = self.storage._format_file_size(total_size) if self.storage else "0 B"
        
        QMessageBox.information(self, "📈 存储统计", 
                               f"总文件数: {total_files:,}\n总大小: {size_str}")
    

    

    
    def show_user_guide(self):
        """Show user guide"""
        QMessageBox.information(self, "用户指南", 
                               "Hash Storage 用户指南:\n\n" +
                               "1. 点击 '连接服务器' 连接到存储\n" +
                               "2. 使用文件树浏览文件和目录\n" +
                               "3. 右键点击文件进行操作\n" +
                               "4. 查看右侧详细信息面板")
    

    
    def copy_info_to_clipboard(self):
        """Copy current info to clipboard"""
        clipboard = QApplication.clipboard()
        text_content = self.info_text.toPlainText()
        if text_content:
            clipboard.setText(text_content)
            self.show_status_message("📋 信息已复制到剪贴板")
        else:
            self.show_status_message("❌ 没有可复制的信息")
    
    def export_info_to_file(self):
        """Export current info to file"""
        if not self.info_text.toPlainText():
            QMessageBox.warning(self, "导出信息", "没有可导出的信息")
            return
        
        from PyQt5.QtWidgets import QFileDialog
        filename, _ = QFileDialog.getSaveFileName(self, "导出信息", 
                                                 "info_export.txt", 
                                                 "Text Files (*.txt)")
        if filename:
            try:
                with open(filename, 'w', encoding='utf-8') as f:
                    f.write(self.info_text.toPlainText())
                self.show_status_message(f"💾 信息已导出到 {filename}")
            except Exception as e:
                QMessageBox.critical(self, "导出错误", f"导出失败: {str(e)}")

def main():
    """Main function"""
    app = QApplication(sys.argv)
    
    # Set application information
    app.setApplicationName("Hash Storage GUI")
    app.setApplicationVersion("1.0.0")
    
    # Create main window
    window = HashStorageGUI()
    window.show()
    
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()